#!/bin/bash
# Script for running JModelica.org test suites.
# Copy this file to somewhere in your PATH.
# You may need to change variables below (in the copy) to match your system.
# Type "tests -h" for usage.
# Requires Unix-like system with bash, grep, dc, mktemp, uname and sed.

merge_path() {
  if [ "$1" = "" ]; then
    if [ "$3" = "" ]; then
      echo $2
    else
      echo $3@SEP@$2
    fi
  else
    echo $1@SEP@$2
  fi
}

# ====== Variables to set ======

# Memory allocation for the ant process
ANT_OPTS="-Xmx1g"

# Default tests - run if no tests are chosen
# Possible values: modelica, optimica, python or combination (e.g. "modelica optimica")
DEFAULT_TESTS="optimica python"

# Default arguments - always parse these arguments before command line
DEFAULT_ARGS=""

# Variables set by configure script
JMODELICA_HOME="@prefix@"
JMODELICA_SRC="@abs_top_srcdir@"
IPOPT_HOME="@IPOPT_HOME@"
SUNDIALS_HOME="@prefix@/ThirdParty/Sundials"
ECLIPSE_HOME="@ECLIPSE_HOME@"
BUILD="@abs_builddir@"
if [[ "${JAVA_HOME}" == "" ]]; then
  JAVA_HOME="@_JAVA_HOME_@"
fi

# ====== End variables to set ======

# Configure Bash
set -o pipefail

CPPAD_HOME="${JMODELICA_HOME}/ThirdParty/CppAD/"
PYTHONPATH="$(merge_path "${PYTHONPATH}" "${JMODELICA_HOME}/Python/")"

# Find operating system
SYS="$(uname -s)"
if [[ "${SYS}" == "Darwin" ]]; then
  SYS=Mac
elif [[ "${SYS:0:5}" == "MINGW" ]]; then
  SYS=Win
fi

# Update library path
if [[ "${SUNDIALS_HOME}" != "" ]]; then
  LIB_PATH="${SUNDIALS_HOME}/lib"
fi
if [[ "${IPOPT_HOME}" != "" ]]; then
  LIB_PATH="$(merge_path "${LIB_PATH}" "${IPOPT_HOME}/lib")"
fi
if [[ "${LIB_PATH}" != "" ]]; then
  LD_LIBRARY_PATH="$(merge_path "${LD_LIBRARY_PATH}" "${LIB_PATH}" /lib)"
fi

# Test sets
tests="modelica optimica python"
test_set_a="modelica optimica python"
test_set_j="modelica optimica"
test_set_m="modelica"
test_set_o="optimica"
test_set_p="python"

# Standard set of tags
TAGS="-a stddist"
if [[ "${IPOPT_HOME}" != "" ]]; then
  TAGS="${TAGS} -a ipopt"
  if [[ "${SYS}" == Win ]]; then
    TAGS="${TAGS} -a windows"
  fi
  if [[ -e "${JMODELICA_HOME}/Python/casadi" ]]; then
    TAGS="${TAGS} -a casadi"
  else
    echo Skipping casadi tests, since CasADi does not seem to be installed.
  fi
fi

# Functions for running specific tests
function modelica_tests() {
  junit_test Modelica
}

function optimica_tests() {
  junit_test Optimica
}

TESTS_DIR="${JMODELICA_HOME}/Python/tests_jmodelica"
function find_python_tests() {
  if [[ "${TESTS}" == "" ]]; then
    TESTS="$(find ${TESTS_DIR} -name \*.py | grep test_)"
  fi
}

function python_tests() {
  if [ "${KEEP_PYTHON}" == 1 ]; then
    TEST_DIR="$(pwd)/tests_$(date "+%F_%H-%M-%S")"
    mkdir ${TEST_DIR}
  else
    TEST_DIR=$(mktemp -dq /tmp/jm_tests.XXXXXX)
  fi 
  print_name Python
  case ${OUTPUT} in
    v)
      if build_jmodelica; then
        find_python_tests
        cd ${TEST_DIR}
        for TEST in ${TESTS}; do
          echo ""
          echo ${TEST} | sed -e 's!^.*jmodelica.!Running !'
          nosetests ${TAGS} -v ${TEST}
        done
      fi 2>&1 | log
      ;;
    q)
      if build_jmodelica 2>&1 | log > /dev/null; then
        find_python_tests
        cd ${TEST_DIR}
        for TEST in ${TESTS}; do
          echo "" | log > /dev/null
          echo ${TEST} | sed -e 's!^.*jmodelica.!Running !' | log > /dev/null
          nosetests ${TAGS} -v ${TEST} 2>&1 \
          | log \
	      | sed -n -e 's!^OK$!PASSED!ip' -e 's!^FAILED !TEST FAILED!p' || echo NOSETESTS_NOT_OK
        done | filter_python_separate
      else
        echo BUILD FAILED
      fi
      ;;
    n)
      if [[ "${NO_BUILD_JMODELICA}" != "1" ]]; then
        echo Building...
      fi
      if build_jmodelica | log > /dev/null; then
        PATTERN="[-=]{40,}"
        find_python_tests
        cd ${TEST_DIR}
        for TEST in ${TESTS}; do
          echo "" | log
          echo ${TEST} | sed -e 's!^.*jmodelica.!Running !' | log
          nosetests ${TAGS} -v ${TEST} 2>&1 \
          | log \
          | egrep -v '^Exception .* ignored$' \
          | egrep -A500 "${PATTERN}" \
          | sed -e 's!^OK$!PASSED!' -e 's!^FAILED !TEST FAILED!' || echo NOSETESTS_NOT_OK
        done | filter_python_separate
      fi
      ;;
  esac
  if [ ! "${KEEP_PYTHON}" == 1 ]; then
    cd ..
    rm -rf ${TEST_DIR}
  fi
}

# Helper functions
function print_name() {
  if [[ "${OUTPUT}" == q ]]; then
    echo -n "$1... "
  else
    echo ======= $1 =======
  fi
}

function log() {
  if [ "${LOG}" == 1 ]; then
      tee -a "${LOGFILE}"
  else
      cat
  fi
}

function filter_python_separate() {
  TMP=$(mktemp -q /tmp/jm_tests.XXXXXX 2>/dev/null) 
  tee ${TMP} | egrep -v "(PASSED|TEST FAILED|NOSETESTS_NOT_OK)"
  cat ${TMP} \
  | sed -n \
        -e '1 i\
            0 0 0' \
        -e 's!^TEST FAILED(errors=\([0-9]*\))!\1+!p' \
        -e 's!^TEST FAILED(failures=\([0-9]*\))!r\1+r!p' \
        -e 's!^TEST FAILED(errors=\([0-9]*\), failures=\([0-9]*\))!\1+r\2+r!p' \
        -e 's!^NOSETESTS_NOT_OK!sar1+rla!p' \
        -e '$ a\
            n[ ]nn[ ]np' \
  | dc \
  | sed \
    -e 's!0 0 [1-9].*!CRASHED!' \
    -e 's!0 0 0!PASSED!' \
	-e 's!\([0-9]\+\) \([0-9]\+\) .*!TEST FAILED (errors=\1, failures=\2)!'
  rm -f ${TMP}
}
  
function junit_test() {
  print_name $1
  cd ${BUILD}/java
  case ${OUTPUT} in
    v)
      ant_test $1 2>&1 \
      | log
      ;;
    q)
      ant_test $1 2>&1 \
      | log \
      | egrep "(\[(jastadd|junit)\]|BUILD FAILED|\[java\] Error when parsing file:)" \
      | sed -n \
        -e '1 i\
            0 0' \
        -e 's!^.*\[jastadd\] \(Semantic errors\|Syntax error\).*$!BUILD FAILED!' \
        -e 's!^.*\[java\] Error when parsing file:.*$!BUILD FAILED!' \
        -e 's!^.*Failures: \([0-9]*\), Errors: \([0-9]*\),.*$!r\1+r\2+!p' \
        -e 's!^.*\(BUILD FAILED\).*$![\1]pq!p' \
        -e '$ a\
            [errors=]nn[, failures=]np' \
      | dc \
      | sed \
        -e 's!errors=0, failures=0!PASSED!' \
	    -e 's!\(errors=.*, failures=.*\)!TEST FAILED (\1)!'
      ;;
    n)
      ant_test $1 \
      | log \
      | egrep "(\[junit\]|\[(java|jastadd)\].*([Ee]rror|[A-Za-z.]*:[1-9][0-9]*))"
      ;;
  esac
}

function ant_test() {
  ant -f ${JMODELICA_SRC}/Compiler/$1Compiler/build.xml ${CLEAN_JUNIT} test
}

function build_jmodelica() {
  if [[ "${NO_BUILD_JMODELICA}" != "1" ]]; then
    if [[ -d ${BUILD} ]]; then
      cd ${BUILD}
      ${CLEAN_BUILD}
      make && make install
    else
      echo Cannot build, build dir does not exist!
      exit 1
    fi
  fi
}

function switch_test_flags() {
  STATE=0
  for a in $*; do
    if [[ "${test[${!a}]}" != 1 ]]; then 
      STATE=1
    fi
  done
  for a in $*; do
    test[${!a}]=${STATE}
  done
}

function read_args() {
  TESTS=
  ARG_TYPE=flag
  for a in ${DEFAULT_ARGS} $*; do
    if [[ "$(echo $a | head -c1)" == "-" ]]; then
      ARG_TYPE=flag
    fi
    FLAG=0
    case ${ARG_TYPE} in
      flag)
        FLAG=1
        ;;
      tag)
        TAGS="${TAGS} -a ${a}"
        ;;
      file)
        if [[ -f "${a}" ]]; then
          FILE="$(readlink -f ${a})"
        else
          FILE="${TESTS_DIR}/${a}"
        fi
        if [[ -f ${FILE} ]]; then
          TESTS="${TESTS} ${FILE}"
        else
          echo "Cannot find test file ${a}"
          exit
        fi
        ;;
    esac
    if [[ ${FLAG} == 1 ]]; then
      for b in $(echo "" ${a}|sed 's!\(.\)!\1 !g'); do
        case ${b} in
          -)
            ;;
          v|q|n)
            OUTPUT=${b}
            ;;
          a|j|m|o|p)
            test_set="test_set_${b}"
            switch_test_flags ${!test_set}
            ;;
          c|C)
            CLEAN_JUNIT=clean
            if [[ "${b}" == C ]]; then
              CLEAN_BUILD="make clean"
            fi
            ;;
          i)
            NO_BUILD_JMODELICA=1
            ;;
          k)
            KEEP_PYTHON=1
            ;;
          l)
            LOG=$((1-LOG))
            ;;
          f)
            ARG_TYPE=file
            ;;
          t)
            TAGS=""
            ARG_TYPE=tag
            ;;
          g)
            ARG_TYPE=tag
            ;;
          h)
            echo "usage: tests [-ajmopvnqcCh] { [-(t|g) [tag1 ...]] } [-f file1 ...]"
            echo "The -ajmop options each control a set of tests, and select all tests in set "
            echo "for running, or, if all in set are already selected, deselects them. "
            echo "Thus, \"tests -am\" runs all tests except modelica. If no tests are chosen, "
            echo "a default set is run (${DEFAULT_TESTS})." 
            echo "  -a  Select/deselect all tests"
            echo "  -j  Select/deselect junit tests"
            echo "  -m  Select/deselect modelica tests"
            echo "  -o  Select/deselect optimca tests"
            echo "  -p  Select/deselect python tests"
            echo "  -v  Verbose output, show everything"
            echo "  -n  Normal output, only show results and compilation errors"
            echo "  -q  Quiet output, only show brief results"
            echo "  -c  Do a clean before junit tests"
            echo "  -C  Do a clean before each test suite"
            echo "  -i  Do not re-build before python tests"
            echo "  -k  Keep generated files from python tests"
            echo "  -l  Toggle creating log file with the equivalent of the output with -v" 
            echo "      (on by default)"
            echo "  -t  When running python tests, only run tests marked with one of the "
            echo "      listed tags. If given without tags, all tests are run. If -t is "
            echo "      given several times, only the last one will be used"
            echo "  -g  When running python tests, add the listed tags to the tags to use"
            echo "  -f  When running python tests, only run tests from the listed files. "
            echo "      Paths relative to the tests directory are accepted, as well as "
            echo "      absolute or relative to current directory"
            echo "  -h  Print this help and exit"
            exit
            ;;
          *)
            echo "Unknown flag -${b}"
            exit
            ;;
        esac
      done
    fi
  done
}

# === Main ===
OUTPUT=n
LOGFILE="$(pwd)/tests.log"
LOG=1
i=0
for a in ${tests}; do
    test[${i}]=0
    (( ${a}=i++ ))
done
export JAVA_HOME JMODELICA_HOME IPOPT_HOME CPPAD_HOME PYTHONPATH LD_LIBRARY_PATH ANT_OPTS SUNDIALS_HOME
TIME=$(date +%s)

read_args $*

if [ "${LOG}" == 1 ]; then
  echo "Log of jm_tests $(date):" > "${LOGFILE}"
fi

ANY_ON=0
for a in ${tests}; do
  if [[ "${test[${!a}]}" == 1 ]]; then
    ANY_ON=1
  fi
done
if [[ "${ANY_ON}" == 0 ]]; then
  switch_test_flags ${DEFAULT_TESTS}
fi

# If on Windows, inform user about bug in ticket #766
if [[ ${SYS} == Win ]]; then
  echo Note: If modelica or optimica tests seem to hang, press enter a couple of times.
fi

for a in ${tests}; do
  if [[ "${test[${!a}]}" == 1 ]]; then
    ${a}_tests
  fi
done

if [[ "${OUTPUT}" != q ]]; then
  echo ========================
fi
echo $(date +%s) ${TIME}"-[Total time: ]nd60/n[:]n60%[0n]sxd10>xp" | dc
